Import yimWork

Extern

#If TARGET="html5" Or TARGET="glfw" Or TARGET="flash"
Function yimMouseWheel:Float() = "yimput_mouseWheel"
Function yimput_init() = "yimput_init"
#EndIf

#If TARGET="html5"
Function yimJoyX:Float(stick:Int=0, unit:Int=0)
Function yimJoyY:Float(stick:Int=0, unit:Int=0)
Function yimJoyZ:Float(stick:Int=0, unit:Int=0)
Function yimJoyDown:Int(button:Int=0, unit:Int=0)
Function yimGamepadUpdate()
	
#ElseIf TARGET="xna"
Function yimMouseWheel:Float() = "yimWork.mouseWheel"
Function yimput_init() = "yimWork.init"
Function yimJoyDown:Int(button:Int=0, unit:Int=0)="yimput.JoyDown"
#EndIf

Public

Const YIMPUT_MOUSE:=1
Const YIMPUT_KEYBOARD:=0
Const YIMPUT_JOY:=2
Const YIMPUT_TOUCH:=3
Const YIMPUT_ONSCREEN:=4

Const YIMPUT_XAXISLOWER:=-1
Const YIMPUT_XAXISUPPER:=-2
Const YIMPUT_YAXISLOWER:=-3
Const YIMPUT_YAXISUPPER:=-4
Const YIMPUT_ZAXISLOWER:=-5
Const YIMPUT_ZAXISUPPER:=-6

Const YIMPUT_OTHERXAXISLOWER:=-7
Const YIMPUT_OTHERXAXISUPPER:=-8
Const YIMPUT_OTHERYAXISLOWER:=-9
Const YIMPUT_OTHERYAXISUPPER:=-10
Const YIMPUT_OTHERZAXISLOWER:=-11
Const YIMPUT_OTHERZAXISUPPER:=-12

Const YIMPUT_HIHATLEFT:=8
Const YIMPUT_HIHATRIGHT:=10
Const YIMPUT_HIHATUP:=9
Const YIMPUT_HIHATDOWN:=11
Const YIMPUT_LEFTSTICKCLICK:=12
Const YIMPUT_RIGHTSTICKCLICK:=13

'summary: The yimputKey object. Very nice!
Class yimputKey
	Field device:Int
	Field key:Int
	Field value:Float
	Field yimEnga:yimEngine
	Field callback:yimputCallback
	
	Field name:String = ""
	
	Field down:Bool = False
	Field bDown:Bool = False
	Field bPressed:Bool = False
	Field bUp:Bool = False
	
	
	Method New(_device:Int, _key:Int, _callback:yimputCallback)
		key = _key
		device = _device
		callback = _callback
	End
	
	
	Method Update()
	
	End
	
	'summary: Allows you to specify the yimputCallback object to call the methods of post creation of this yimputKey
	Method SetCallback(cb:yimputCallback)
		callback = cb
	End
	
	Method SetName(_name:String)
		name = _name
	End
	
	'summary: Returns whether or not this key is currently down. Use this if you don't specify a callback
	Method IsDown:Bool()
		Return down
	End
	
	'summary: Returns whether or not this key has has been pressed. Will only return True once until the key is released and pressed again. Use this if you don't specify a callback
	Method IsPressed:Bool()
		If bDown
			bDown = False
			Return True 
		EndIf
		Return False
	End
	
	'summary: Returns whether or not this key has been released. Use this if you don't specify a callback
	Method IsUp:Bool()
		If bUp
			bUp = False
			Return True
		EndIf
		Return False
	End
	
	'summary: Returns the Float representation of this input's value. Returns 0 or 1 for keys and buttons, 0.0 - 1.0 for joystick axes, and 0.0 to some number for mouse axes
	Method GetValue:Float()
		Return value
	End

End

Class yimputOnScreenButton
	
End

Class yimputOnScreenStick
	
End

#REM
	summary: The yimputCallback. Very useful. I swear.
	
	To create the callback, you Extend this object.
	
	Once applied to a yimputKey, the Down(), Pressed(), and Up() methods will be called as necessary
#END
Class yimputCallback
	Method Down()
	
	End
	
	Method Pressed()
		
	End
	
	Method Up()
	
	End
End

Class yimputProfile
	Field name:String
	Field yimputList:List<yimputObject> = New List<yimputObject>
	
	Method New(_name:String)
		name = _name
	End
End

#REM
	summary: This is the input manager for yimWork
	
	When you create the yimputManager object, you can send it the yimEngine reference
	and yimEngine will take care of the rest, otherwise, you will need to call
	your yimputManager object's Update() method at the beginning of your OnUpdate() call
	and UpdateMouse() at the end of your OnUpdate() call if you intend to use the mouse.
#END
Class yimputManager
	Field profileList:List<yimputProfile> = New List<yimputProfile>()
	Field curProfile:yimputProfile
	'Field onScreenList:List<yimputOnScreenInput> = New List<yimputOnScreenInput>()
	Field keyList:List<yimputKey> = New List<yimputKey>()
	
	Field touchScreenOn:Bool = False
	Field maxTouches:Int = 1
	
	Field eng:yimEngine
	
	Field yimputKeyLiterals:IntMap<String>
	Field yimputAxisNames:IntMap<String>
	
	Field newMouse:yimVec2D = New yimVec2D(0, 0)
	Field oldMouse:yimVec2D = New yimVec2D(0, 0)
	
	Field mmX:Int
	Field mmY:Int
	Field mmZ:Int
	
	Field mouseAxisThreshold:Float = 0.2
	Field joyAxisThreshold:Float = 0.2
	
	Method New(_eng:yimEngine=Null)
		#If TARGET="xna" Or TARGET="glfw" Or TARGET="html5" Or TARGET="flash"
		yimput_init()
		#EndIf
		
		curProfile = AddProfile("Default")
		eng = _eng
		If eng
			eng.LinkInputManager(Self)
		EndIf
		
		yimputKeyLiterals = New IntMap<String>()
	
		yimputKeyLiterals.Set(KEY_0, "0")
		yimputKeyLiterals.Set(KEY_1, "1")
		yimputKeyLiterals.Set(KEY_2, "2")
		yimputKeyLiterals.Set(KEY_3, "3")
		yimputKeyLiterals.Set(KEY_4, "4")
		yimputKeyLiterals.Set(KEY_5, "5")
		yimputKeyLiterals.Set(KEY_6, "6")
		yimputKeyLiterals.Set(KEY_7, "7")
		yimputKeyLiterals.Set(KEY_8, "8")
		yimputKeyLiterals.Set(KEY_9, "9")
		
		yimputKeyLiterals.Set(KEY_Q, "Q")
		yimputKeyLiterals.Set(KEY_W, "W")
		yimputKeyLiterals.Set(KEY_E, "E")
		yimputKeyLiterals.Set(KEY_R, "R")
		yimputKeyLiterals.Set(KEY_T, "T")
		yimputKeyLiterals.Set(KEY_Y, "Y")
		yimputKeyLiterals.Set(KEY_U, "U")
		yimputKeyLiterals.Set(KEY_I, "I")
		yimputKeyLiterals.Set(KEY_O, "O")
		yimputKeyLiterals.Set(KEY_P, "P")
		yimputKeyLiterals.Set(KEY_A, "A")
		yimputKeyLiterals.Set(KEY_S, "S")
		yimputKeyLiterals.Set(KEY_D, "D")
		yimputKeyLiterals.Set(KEY_F, "F")
		yimputKeyLiterals.Set(KEY_G, "G")
		yimputKeyLiterals.Set(KEY_H, "H")
		yimputKeyLiterals.Set(KEY_J, "J")
		yimputKeyLiterals.Set(KEY_K, "K")
		yimputKeyLiterals.Set(KEY_L, "L")
		yimputKeyLiterals.Set(KEY_Z, "Z")
		yimputKeyLiterals.Set(KEY_X, "X")
		yimputKeyLiterals.Set(KEY_C, "C")
		yimputKeyLiterals.Set(KEY_V, "V")
		yimputKeyLiterals.Set(KEY_B, "B")
		yimputKeyLiterals.Set(KEY_N, "N")
		yimputKeyLiterals.Set(KEY_M, "M")
		
		yimputKeyLiterals.Set(KEY_F1, "F1")
		yimputKeyLiterals.Set(KEY_F2, "F2")
		yimputKeyLiterals.Set(KEY_F3, "F3")
		yimputKeyLiterals.Set(KEY_F4, "F4")
		yimputKeyLiterals.Set(KEY_F5, "F5")
		yimputKeyLiterals.Set(KEY_F6, "F6")
		yimputKeyLiterals.Set(KEY_F7, "F7")
		yimputKeyLiterals.Set(KEY_F8, "F8")
		yimputKeyLiterals.Set(KEY_F9, "F9")
		yimputKeyLiterals.Set(KEY_F10, "F10")
		yimputKeyLiterals.Set(KEY_F11, "F11")
		yimputKeyLiterals.Set(KEY_F12, "F12")
		
		yimputKeyLiterals.Set(KEY_TILDE, "`")
		yimputKeyLiterals.Set(KEY_TAB, "TAB")
		yimputKeyLiterals.Set(KEY_SHIFT, "SHIFT")
		yimputKeyLiterals.Set(KEY_CONTROL, "CTRL")
		yimputKeyLiterals.Set(KEY_SPACE, "SPACE")
		yimputKeyLiterals.Set(KEY_MINUS, "-")
		yimputKeyLiterals.Set(KEY_EQUALS, "=")
		yimputKeyLiterals.Set(KEY_OPENBRACKET, "[")
		yimputKeyLiterals.Set(KEY_CLOSEBRACKET, "]")
		yimputKeyLiterals.Set(KEY_SEMICOLON, ";")
		yimputKeyLiterals.Set(KEY_QUOTES, "'")
		yimputKeyLiterals.Set(KEY_COMMA, ",")
		yimputKeyLiterals.Set(KEY_PERIOD, ".")
		yimputKeyLiterals.Set(KEY_BACKSLASH, "\")
		yimputKeyLiterals.Set(KEY_SLASH, "/")
		yimputKeyLiterals.Set(KEY_BACKSPACE, "BACKSPACE")
		yimputKeyLiterals.Set(KEY_ESCAPE, "ESCAPE")
		yimputKeyLiterals.Set(KEY_ENTER, "ENTER")
		
		yimputKeyLiterals.Set(KEY_INSERT, "INSERT")
		yimputKeyLiterals.Set(KEY_DELETE, "DELETE")
		yimputKeyLiterals.Set(KEY_HOME, "HOME")
		yimputKeyLiterals.Set(KEY_END, "END")
		yimputKeyLiterals.Set(KEY_PAGEUP, "PAGEUP")
		yimputKeyLiterals.Set(KEY_PAGEDOWN, "PAGEDOWN")
		yimputKeyLiterals.Set(KEY_UP, "UP")
		yimputKeyLiterals.Set(KEY_DOWN, "DOWN")
		yimputKeyLiterals.Set(KEY_LEFT, "LEFT")
		yimputKeyLiterals.Set(KEY_RIGHT, "RIGHT")
		
		yimputAxisNames = New IntMap<String>()
		
		yimputAxisNames.Set(YIMPUT_XAXISLOWER, "X-Axis Lower")
		yimputAxisNames.Set(YIMPUT_XAXISUPPER, "X-Axis Upper")
		yimputAxisNames.Set(YIMPUT_YAXISLOWER, "Y-Axis Lower")
		yimputAxisNames.Set(YIMPUT_YAXISUPPER, "Y-Axis Upper")
		yimputAxisNames.Set(YIMPUT_ZAXISLOWER, "Z-Axis Lower")
		yimputAxisNames.Set(YIMPUT_ZAXISUPPER, "Z-Axis Upper")
		
		yimputAxisNames.Set(YIMPUT_OTHERXAXISLOWER, "OTHER X-Axis Lower")
		yimputAxisNames.Set(YIMPUT_OTHERXAXISUPPER, "OTHER X-Axis Upper")
		yimputAxisNames.Set(YIMPUT_OTHERYAXISLOWER, "OTHER Y-Axis Lower")
		yimputAxisNames.Set(YIMPUT_OTHERYAXISUPPER, "OTHER Y-Axis Upper")
		yimputAxisNames.Set(YIMPUT_OTHERZAXISLOWER, "OTHER Z-Axis Lower")
		yimputAxisNames.Set(YIMPUT_OTHERZAXISUPPER, "OTHER Z-Axis Upper")
		
		yimputAxisNames.Set(YIMPUT_HIHATLEFT, "Hi-Hat Left")
		yimputAxisNames.Set(YIMPUT_HIHATRIGHT, "Hi-Hat Right")
		yimputAxisNames.Set(YIMPUT_HIHATUP, "Hi-Hat Up")
		yimputAxisNames.Set(YIMPUT_HIHATDOWN, "Hi-Hat Down")		
	End
	
	'summary: Adds an input profile. This is good for having the same keys perform different actions in different states of the game
	Method AddProfile:yimputProfile(name:String)
		If GetProfile(name) = Null
			Local tProf:yimputProfile = New yimputProfile(name)
			profileList.AddLast(tProf)
			Return tProf
		EndIf
		LogMe("Error! Input profile '"+name+"' already exists.")
		Return Null
	End
	
	'summary: Gets a yimputProfile by name.
	Method GetProfile:yimputProfile(name:String)
		For Local tP:= EachIn profileList
			If tP.name = name
				Return tP
			EndIf
		Next
		Return Null
	End
	
	'summary: Sets the current input profile to check the key states of
	Method SetCurrentProfile(prof:yimputProfile)
		curProfile = prof
	End
	
	'summary: Sets the current input profile to check the key states of, by name
	Method SetCurrentProfileByName(name:String)
		curProfile = GetProfile(name)
		If curProfile = Null
		
		EndIf
	End
	
	#REM
		summary: Add a key!
		
		Useable devices are the constants:
		
		YIMPUT_KEYBOARD
		YIMPUT_MOUSE
		YIMPUT_JOY
		YIMPUT_TOUCH
		[i]YIMPUT_ONSCREEN (no worky yet)[/i]
		
		Keys are:
		
		Monkey KEY_*, MOUSE_*, and JOY_* constants. Or 0, 1, 2, 3, etc......
		
		Also:
		
		YIMPUT_HIHATLEFT
		YIMPUT_HIHATRIGHT
		YIMPUT_HIHATUP
		YIMPUT_HIHATDOWN
		YIMPUT_LEFTSTICKCLICK
		YIMPUT_RIGHTSTICKCLICK
		
		You can also pass an axis as the key parameter and it acts like a key:
		
		YIMPUT_XAXISLOWER (works for mouse and joystick)
		YIMPUT_XAXISUPPER (works for mouse and joystick)
		YIMPUT_YAXISLOWER (works for mouse and joystick)
		YIMPUT_YAXISUPPER (works for mouse and joystick)
		YIMPUT_ZAXISLOWER
		YIMPUT_ZAXISUPPER
		
		YIMPUT_OTHERXAXISLOWER
		YIMPUT_OTHERXAXISUPPER
		YIMPUT_OTHERYAXISLOWER
		YIMPUT_OTHERYAXISUPPER
		YIMPUT_OTHERZAXISLOWER
		YIMPUT_OTHERZAXISUPPER
		
		[b]Joystick only works on glfw and xna targets.[/b]
		
	#END
	Method AddKey:yimputKey(device:Int, key:Int, callback:yimputCallback=Null)
		Local tKey:yimputKey = New yimputKey(device, key, callback)
		keyList.AddLast(tKey)
		Return tKey
	End
	
	Method AddOnScreenInput(osi:yimputOnScreenInput)
		onScreenList.AddLast(osi)
	End
	
	Method AddOnScreenButton:yimputOnScreenButton(xPos:Float, yPos:Float, imageFile:Image=Null, _width:Float=0, _height:Float=0)
		Local tButton:yimputOnScreenButton = New yimputOnScreenButton(eng, xPos, yPos, imageFile, _width, _height)
		onScreenList.AddLast(tButton)
		Return tButton
	End
	
	'summary: This tells the yimputManager that you want to check for multiple touches
	Method SetTouchScreenOn(toggle:Bool)
		touchScreenOn = toggle
	End
	
	'summary: Set the number of touches to check. (0 - 32)
	Method SetMaxTouches(touches:Int)
		maxTouches = touches
	End
	
	Method DoDown(key:yimputKey)
		If Not key.down
			If key.callback
				key.callback.Pressed()
			EndIf
			
			key.bDown = True
		EndIf
		
		key.down = True
		
		If key.callback
			key.callback.Down()
		EndIf
	End
	
	Method DoUp(key:yimputKey)
		If key.down
			If key.callback
				key.callback.Up()
			EndIf
			key.bUp = True
			key.bDown = False
			key.down = False
		EndIf
	End
	
	'summary: Must be called at the end of your OnUpdate() call if you haven't set up this yimputManager to be controlled by yimEngine and intend to use the mouse
	Method UpdateMouse()
		oldMouse.Set(newMouse.x, newMouse.y)
	End
	
	'summary: Must be called at the beginning of your OnUpdate() call if you haven't set up this yimputManager to be controlled by yimEngine
	Method Update()
		#If TARGET="html5"
		yimGamepadUpdate()
		#EndIf
		
		newMouse.Set(MouseX(), MouseY())
		mmX = MouseMoveX()
		mmY = MouseMoveY()
		
		#If TARGET="xna" Or TARGET="glfw" Or TARGET="html5" Or TARGET="flash"
		mmZ = yimMouseWheel()
		#Else
		mmZ = 0
		#EndIf
		
		UpdateOnScreenInputs()
		
		For Local tKey:= EachIn keyList
			Select tKey.device
				Case YIMPUT_KEYBOARD
					If KeyDown(tKey.key)
						tKey.value = 1
						DoDown(tKey)
					Else
						tKey.value = 0
						DoUp(tKey)
					EndIf
				Case YIMPUT_MOUSE
					If tKey.key >= 0
						If MouseDown(tKey.key)
							tKey.value = 1
							DoDown(tKey)
						Else
							tKey.value = 0
							DoUp(tKey)
						EndIf
					Else
						tKey.value = MouseAxisValue(tKey.key)
						If tKey.value >= mouseAxisThreshold
							DoDown(tKey)
						Else
							DoUp(tKey)
						EndIf
					EndIf
				Case YIMPUT_JOY
					If tKey.key >= 0
						If y_JoyDown(tKey.key)
							tKey.value = 1
							DoDown(tKey)
						Else
							DoUp(tKey)
							tKey.value = 0
						EndIf
					Else
						tKey.value = JoyAxisValue(tKey.key)
						If tKey.value >= joyAxisThreshold
							DoDown(tKey)
						Else
							DoUp(tKey)
						EndIf
					EndIf
			End
		Next
		

	End
	
	Method UpdateOnScreenInputs()
		If touchScreenOn

		EndIf
	End
	
	'summary: Checks for any input from the user and returns a yimputKey object that reflects the last input detected. Perfect for allowing the user to set up custom controls
	Method CheckForAny:yimputKey()
		Local curDevice:Int = -1
		Local curKey:Int
		
		For Local k:=EachIn yimputKeyLiterals.Keys
			If KeyDown(k)
				Debug("Key Down: "+yimputKeyLiterals.Get(k))
				curDevice = YIMPUT_KEYBOARD
				curKey = k
			EndIf
		Next
		
		For Local b = 0 To 3
			If MouseDown(b)
				Debug("Mouse Button: "+b)
				curDevice = YIMPUT_MOUSE
				curKey = b
			EndIf
		Next
		
		For Local a = -1 To -6 Step -1
			Local val:Float = MouseAxisValue(a)
			If val > mouseAxisThreshold
				Debug("Mouse Axis: "+yimputAxisNames.Get(a)+" Val: "+val)
				curDevice = YIMPUT_MOUSE
				curKey = a
			EndIf
		Next
		
		For Local b = 0 To 15
			If y_JoyDown(b)
				Debug("Joy Button: "+b)
				curDevice = YIMPUT_JOY
				curKey = b
			EndIf
		Next
		
		For Local a = -1 To -12 Step -1
			Local val:Float = JoyAxisValue(a)
			If val > joyAxisThreshold
				Debug("Joy Axis: "+yimputAxisNames.Get(a)+" Val: "+val)
				curDevice = YIMPUT_JOY
				curKey = a
			EndIf
		Next
		
		If curDevice > -1
			Return New yimputKey(curDevice, curKey, Null)
		Else
			Return Null
		EndIf
	End
	
	Method RenderOnScreenInputs()
		For Local tOSI:= EachIn onScreenList
			tOSI.Render()
		Next
	End
	
	Method JoyAxisValue:Float(axis:Int)
		Local ret:Float = 0.0
		
		Select axis
			Case -1
				ret = 0-y_JoyX()
			Case -2
				ret = y_JoyX()
			Case -3
				ret = 0-y_JoyY()
			Case -4
				ret = y_JoyY()
			Case -5
				ret = 0-y_JoyZ()
			Case -6
				ret = y_JoyZ()

			Case -7
				ret = 0-y_JoyX(1)
			Case -8
				ret = y_JoyX(1)
			Case -9
				ret = 0-y_JoyY(1)
			Case -10
				ret = y_JoyY(1)
			Case -11
				ret = 0-y_JoyZ(1)
			Case -12
				ret = y_JoyZ(1)
		End
		
		If ret < 0.0 Then Return 0.0
		
		Return ret
	End
	
	Method y_JoyX:Float(stick:Int=0, unit:Int=0)
		#If TARGET="html5"
		Return yimJoyX(stick, unit)
		#Else
		Return JoyX(stick, unit)
		#EndIf
	End
	
	Method y_JoyY:Float(stick:Int=0, unit:Int=0)
		#If TARGET="html5"
		Return yimJoyY(stick, unit)
		#Else
		Return JoyY(stick, unit)
		#EndIf
	End
	
	Method y_JoyZ:Float(stick:Int=0, unit:Int=0)
		#If TARGET="html5"
		Return yimJoyZ(stick, unit)
		#Else
		Return JoyZ(stick, unit)
		#EndIf
	End
	
	Method y_JoyDown:Int(button:Int=0, unit:Int=0)
		#If TARGET="html5" Or TARGET="xna"
		Return yimJoyDown(button, unit)
		#Else
		Return JoyDown(button, unit)
		#EndIf	
	End
	
	Method MouseAxisValue:Float(axis:Int)
		Local ret:Float = 0.0
		
		Select axis
			Case -1
				ret = mmX/-25.4
			Case -2
				ret = mmX/25.4
			Case -3
				ret = mmY/-25.4
			Case -4
				ret = mmY/25.4
			Case -5
				ret = 0-mmZ
			Case -6
				ret = mmZ
		End
		
		If ret < 0 Then Return 0
		
		Return ret
	End
	
	'summary: Returns the amount the mouse moved in the x direction since the last update
	Method MouseMoveX:Int()
		Return (newMouse.x - oldMouse.x)
	End
	
	'summary: Returns the amount the mouse moved in the y direction since the last update
	Method MouseMoveY:Int()
		Return (newMouse.y - oldMouse.y)
	End
End
